package com.pan.api.utils;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;

//PānQíĀn De Json Convert
public enum PanJsonConvertUtilImpl implements PanJsonConvertUtil {
    INSTANCE;
    private final String nameTypeTemplate = "\"%s\": %s";
    private final String lineTemplate = "%s,\n%s";
    private final String jsonTemplate = "{\n{list}\n}";
    private final String genericTemple = "{\n\"type\": \"{typeName}\",\n\"generics\":\n{\n{list}\n}\n}";

    private final Function<String, String> generateJson = (list) -> jsonTemplate.replace("{list}", list);
    private final BiFunction<String, String, String> generateCommonNameType = (name, type) -> String.format(nameTypeTemplate, name, "\"" + type + "\"");
    private final BiFunction<String, String, String> generateObjectNameType = (name, type) -> String.format(nameTypeTemplate, name, "\n" + type);
    private final BiFunction<String, String, String> concatenateNameType = (previous, current) -> String.format(lineTemplate, previous, current);
    private final BiFunction<String, String, String> generateGenericJson = (typeName, list) -> genericTemple.replace("{typeName}", typeName).replace("{list}", list);

    //The function deals with the generic types.
    private String genericTypeHandle(Field field) {
        Type genericType = field.getGenericType();
        if (genericType instanceof ParameterizedType parameterizedType) {
            String list = "";
            Class<?> clazz = field.getType();
            if (clazz.isArray()) clazz = clazz.getComponentType();
            String typeName = clazz.getName();
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            for (Type type : actualTypeArguments) {
                String key = type.getTypeName();
                String item = "";
                if (key.startsWith("java.")) {
                    String value = "base types";
                    item = generateCommonNameType.apply(key, value);
                } else if (key.indexOf(".")==-1) {
                    item = generateCommonNameType.apply(key, key);
                } else {
                    try {
                        Class<?> c = Class.forName(key);
                        String value = INSTANCE.baseConvert(c);
                        item = generateObjectNameType.apply(key, value);
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    }
                }
                list = list.isEmpty() ? item : concatenateNameType.apply(list, item);
            }
            return generateGenericJson.apply(typeName, list);
        } else {
            throw new RuntimeException("This type is not generic types");
        }
    }

    @Override
    public String convertToJson(Class<?> tar) {
        return INSTANCE.jsonFormat(INSTANCE.baseConvert(tar));
    }



    private String baseConvert(Class<?> tar) {
        if (tar.getName().startsWith("java.")||tar.isPrimitive()||tar.isEnum()) return tar.getName();
        Field[] declaredFields = tar.getDeclaredFields();
        String list = "";
        for (Field declaredField : declaredFields) {
            String name = declaredField.getName();
            Class<?> type = declaredField.getType();
            String value = type.getTypeName();
            String itemStr = "";
            if (type.isArray()) {//
                name += "[]";
                type = type.getComponentType();
            }
            BiFunction<String, String, String> useBiFunc = null;
            if (type.isPrimitive() //base type
                    || type.isEnum()  //enum type can‘t be reflected
                    || value.startsWith("java.")) {
                if (declaredField.getGenericType() instanceof ParameterizedType) {
                    value = genericTypeHandle(declaredField);
                    itemStr = generateObjectNameType.apply(name, value);
                } else {
                    itemStr = generateCommonNameType.apply(name, value);
                }
            } else {
                if (declaredField.getGenericType() instanceof ParameterizedType) {
                    value = genericTypeHandle(declaredField);
                } else {
                    value = INSTANCE.baseConvert(type);
                }
                itemStr = generateObjectNameType.apply(name, value);
            }
            //
            list = list.isEmpty() ? itemStr : concatenateNameType.apply(list, itemStr);
        }
        return generateJson.apply(list);
    }


    private final String basePrevious = "\t";
    private final Function<Integer, String> generateRetraction = count -> {
//        System.out.println("debug count: "+count);
        String result = "";
        for (int i = 0; i < count; i++) result += basePrevious;
        return result;
    };

    private String insertString(String in, int position, String target) {
        if (position >= target.length())
            throw new RuntimeException("Error: Insert the string at a position more than the target string length");
        if (position < 0)
            throw new RuntimeException("Error: The position for inserting the string is negative");
        return target.substring(0, position) + in + target.substring(position);
    }

    private final String lineHeadRegex = "^\\s*{";
    private final String lineTailRegex = "}\\s*$";

    @Override
    public String jsonFormat(String json) {
/**
 *       I give up on do this.
 *       I might do it later, but for now I have give up
 *       todo: Completely format the json string
 *
 *      approach: Mask the string content of the json string
 *          -> clean the json string by removing spaces and line breaks to consolidate it into a single line of text.
 *          -> place each curly brace on a new line, and break lines at commas.
 *          (--> in order to reuse previous code, this step is broken down, although it reduces code efficiency) and apply indentation.
 *
 *       //PānQíĀn: English 太难了， 还是写中文吧
 *       //暂时放弃了
 *       todo 未来思路：对json字符串的字符串内容进行替换屏蔽 -> 对json字符进行清洗，洗去空格、换行，从而统一为一行字符
 *                    -> 将每个花括号独立为一行、遇到逗号就换行
 *                    (-->这里为了复用之前的代码，把这个步骤拆开，虽然代码效率会降低)进行缩进
 */
        //So be it,
        int currentPreviousCount = 0;
        StringBuffer sb = new StringBuffer();
        for (String line : json.split("\n")) {
            if (line.charAt(0) == '}') currentPreviousCount--;
            String retraction = generateRetraction.apply(currentPreviousCount);
            if (line.charAt(0) == '{') currentPreviousCount++;
            String newLine = retraction + line + "\n";
            sb.append(newLine);
        }
        return sb.length() > 1?sb.substring(0,sb.length()-1).toString():sb.toString();
    }

    public static void main(String[] args) {

        class InInInner {
            String str;
        }
        class InInner {
            int a;
            String b;
            InInInner inInInner;
        }
        ;
        class TempInner {
            List<InInner> list;
            String bbb;
            int[] arr;
            InInner[] inInner;
        }
        String json = PanJsonConvertUtilImpl.INSTANCE.convertToJson(TempInner.class);
        System.out.println(json);
//        System.out.println(PanJsonStandardization.INSTANCE.jsonFormat(json));
    }
}
